// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace Microsoft.TeamFoundation.Migration.Toolkit.Wit
{
    /// <summary>
    /// Work item types collection.
    /// </summary>
    class InternalWorkItemTypes
    {
        private SyncProcess m_sharedData;                   // Shared data object
        private object m_locker;                            // Synchronization object
        private Pair<Dictionary<string, InternalWorkItemType>> m_types; // Types  map
        private List<string> m_syncedTypes;                 // List of work item types that have been synchronized

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="sharedData">Shared data object</param>
        public InternalWorkItemTypes(
            SyncProcess sharedData)
        {
            m_sharedData = sharedData;
            m_locker = new object();
            m_types = new Pair<Dictionary<string, InternalWorkItemType>>(
                new Dictionary<string, InternalWorkItemType>(m_sharedData.Stores.Left.StringValueComparer),
                new Dictionary<string, InternalWorkItemType>(m_sharedData.Stores.Right.StringValueComparer));
            m_syncedTypes = new List<string>();

            ReadOnlyCollection<WorkItemTypeMapping> types = m_sharedData.Session.WorkItemTypeMappings;

            for (int i = 0; i < types.Count; i++)
            {
                InternalWorkItemType type = InternalWorkItemType.Create(sharedData, types[i]);

                m_types.Left.Add(type.Names.Left, type);
                m_types.Right.Add(type.Names.Right, type);
            }
        }

        /// <summary>
        /// Finds a work item type with the given name. If such item does not exist, it gets cached.
        /// </summary>
        /// <param name="side">Side to look for the type</param>
        /// <param name="name">Type name</param>
        /// <returns>Work item type object</returns>
        public InternalWorkItemType this[
            Side side,
            string name]
        {
            get
            {
                InternalWorkItemType type;

                lock (m_locker)
                {
                    if (!m_types[side].TryGetValue(name, out type))
                    {
                        type = InternalWorkItemType.Create(m_sharedData, name);
                        m_types.Left.Add(type.Names.Left, type);
                        try
                        {
                            m_types.Right.Add(type.Names.Right, type);
                        }
                        catch
                        {
                            m_types.Left.Remove(type.Names.Left);
                            throw;
                        }
                    }
                    Side s = !m_sharedData.TfsSide;
                    int pos = m_syncedTypes.BinarySearch(type.Names[s], m_sharedData.Stores[s].StringValueComparer);

                    if (pos < 0)
                    {
                        TfsCore core = ((TfsMigrationWorkItemStore)m_sharedData.Stores[!s]).Core;

                        // Synchronize the work item type
                        core.SyncWorkItemType(m_sharedData, type);
                        m_syncedTypes.Insert(~pos, type.Names[s]);
                    }
                }
                return type;
            }
        }

        /// <summary>
        /// Gets the shared data object.
        /// </summary>
        internal SyncProcess SharedData { get { return m_sharedData; } }
    }
}
